Skip to content

BE-FEAT-게시판-하위-목록-및-삭제-기능-추가#146

Merged
Kosw6 merged 1 commit intomainfrom
BE-FEAT-게시판-하위-목록-및-삭제-기능-추가
Nov 26, 2025

Hidden character warning

The head ref may contain hidden characters: "BE-FEAT-\uac8c\uc2dc\ud310-\ud558\uc704-\ubaa9\ub85d-\ubc0f-\uc0ad\uc81c-\uae30\ub2a5-\ucd94\uac00"
Merged

BE-FEAT-게시판-하위-목록-및-삭제-기능-추가#146
Kosw6 merged 1 commit intomainfrom
BE-FEAT-게시판-하위-목록-및-삭제-기능-추가

Conversation

@Kosw6
Copy link
Collaborator

@Kosw6 Kosw6 commented Nov 26, 2025

  1. 하위 게시판 목록 확인 API 생성
    • 엔드포인트 : GET /api/board/childs
    • 부모게시판ID가 존재하는 게시판 목록을 반환
  2. 게시판 삭제 API 생성
    • 엔드포인트 : DELETE /api/board/{boradId}
    • 부모 게시판, 하위 게시판 상관없이 게시판과 연관된 게시물, 첨부파일등 정보와 함께 삭제
    • 부모 게시판의 경우 연관된 하위 게시판까지 삭제
    • UserRole : PRESIDENT만 게시판 삭제 가능
    • PostIdUserIdProjection 객체 생성하여 JPA에서 게시판과 연관된 게시물과 작성한 사용자의 ID를 받아옴
  • 추가된 엔티티 없음

Summary by CodeRabbit

릴리스 노트

  • 새로운 기능
    • 자식 게시판 조회 기능 추가
    • 게시판 삭제 기능 추가 (하위 게시판 및 관련 게시물 포함 삭제)
    • 게시판 소유자 권한 검증 기능 강화

✏️ Tip: You can customize this high-level summary in your review settings.

@Kosw6 Kosw6 requested a review from discipline24 as a code owner November 26, 2025 07:07
@coderabbitai
Copy link

coderabbitai bot commented Nov 26, 2025

개요

자식 게시판 조회 및 게시판 삭제 기능을 추가하는 변경으로, 새로운 컨트롤러 엔드포인트 두 개, 저장소 쿼리 메서드 세 개, 서비스 메서드 두 개, 그리고 프로젝션 인터페이스 하나를 도입합니다.

변경 사항

코호트 / 파일 변경 요약
컨트롤러 엔드포인트 확장
backend/src/main/java/org/sejongisc/backend/board/controller/BoardController.java
getChildBoards() 메서드 추가(GET /api/board/childs)로 자식 게시판 목록 반환, deleteBoard() 메서드 추가(DELETE /api/board/{boardId})로 게시판 삭제 기능 제공
저장소 쿼리 메서드 추가
backend/src/main/java/org/sejongisc/backend/board/repository/BoardRepository.java
findAllByParentBoardIsNotNull()findAllByParentBoard_BoardId(UUID) 메서드 추가로 부모 게시판 기반 자식 게시판 조회 지원
게시물 저장소 및 프로젝션
backend/src/main/java/org/sejongisc/backend/board/repository/PostRepository.java, backend/src/main/java/org/sejongisc/backend/board/repository/projection/PostIdUserIdProjection.java
findPostIdAndUserIdByBoardId() 쿼리 메서드 추가, 신규 PostIdUserIdProjection 인터페이스로 게시물 ID와 사용자 ID 쌍 조회
서비스 계층 확장
backend/src/main/java/org/sejongisc/backend/board/service/PostService.java, backend/src/main/java/org/sejongisc/backend/board/service/PostServiceImpl.java
getChildBoards()deleteBoard(UUID, UUID) 메서드 추가로 자식 게시판 조회와 캐스케이딩 삭제(게시판 및 소속 게시물) 구현
예외 처리 확장
backend/src/main/java/org/sejongisc/backend/common/exception/ErrorCode.java
INVALID_BOARD_OWNER 예외 코드 추가로 게시판 수정/삭제 권한 검증 지원

시퀀스 다이어그램

sequenceDiagram
    participant Client
    participant BoardController
    participant PostService
    participant BoardRepository
    participant PostRepository
    participant Database

    Client->>BoardController: DELETE /api/board/{boardId}
    BoardController->>PostService: deleteBoard(boardId, userId)
    PostService->>PostService: validateBoardOwner(boardId, userId)
    PostService->>BoardRepository: findAllByParentBoard_BoardId(boardId)
    BoardRepository->>Database: Query child boards
    Database-->>BoardRepository: List<Board>
    PostService->>PostRepository: findPostIdAndUserIdByBoardId(boardId)
    PostRepository->>Database: Query posts with IDs
    Database-->>PostRepository: List<PostIdUserIdProjection>
    PostService->>Database: Delete posts
    PostService->>Database: Delete boards (cascade)
    Database-->>PostService: Success
    PostService-->>BoardController: Success
    BoardController-->>Client: 200 OK
Loading

예상 코드 리뷰 난이도

🎯 3 (중간) | ⏱️ ~20분

  • 검토 필요 영역:
    • PostServiceImpl.deleteBoard()의 캐스케이딩 삭제 로직 및 권한 검증 구현 정확성
    • 부모-자식 게시판 관계 쿼리(findAllByParentBoard_BoardId())와 재귀적 삭제 처리의 완결성
    • PostIdUserIdProjection 프로젝션 매핑이 JPA/Hibernate에서 올바르게 작동하는지 확인
    • 컨트롤러의 인증 보안 확인과 서비스 위임 흐름의 적절성

관련 가능성 있는 PR

제안하는 리뷰어

  • discipline24

🐰 자식 게시판 찾아내고,
삭제 권한 단단히 확인하고,
캐스케이드로 쓸어내는 마법,
프로젝션으로 담아내는 지혜,
게시판 정리, 깔끔하고 우아해! ✨

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 66.67% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed PR 제목이 변경사항의 핵심을 명확하게 반영합니다. 하위 게시판 목록 조회 및 삭제 기능 추가라는 주요 변경사항을 정확히 설명하고 있습니다.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch BE-FEAT-게시판-하위-목록-및-삭제-기능-추가

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@Kosw6 Kosw6 merged commit d3e7963 into main Nov 26, 2025
1 check was pending
@Kosw6 Kosw6 deleted the BE-FEAT-게시판-하위-목록-및-삭제-기능-추가 branch November 26, 2025 07:09
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🧹 Nitpick comments (7)
backend/src/main/java/org/sejongisc/backend/common/exception/ErrorCode.java (1)

106-108: 불필요한 빈 줄 정리

마지막 enum 상수와 필드 선언 사이에 빈 줄이 3개 있습니다. 코드 일관성을 위해 1개로 줄이는 것을 권장합니다.

  INVALID_BOARD_TYPE(HttpStatus.BAD_REQUEST, "상위 게시판에는 글을 작성할 수 없습니다.");

-
-
-
  private final HttpStatus status;
backend/src/main/java/org/sejongisc/backend/board/service/PostService.java (1)

36-38: LGTM - 메서드 주석 추가 권장

새로운 메서드들이 PR 목표에 맞게 추가되었습니다. 다만, 다른 메서드들과의 일관성을 위해 deleteBoard 메서드에도 주석을 추가하는 것을 권장합니다.

  // 하위 게시판 목록 조회
  List<BoardResponse> getChildBoards();
+  // 게시판 삭제
  void deleteBoard(UUID boardId, UUID boardUserId);
backend/src/main/java/org/sejongisc/backend/board/controller/BoardController.java (3)

169-172: 엔드포인트 경로 수정 및 미사용 파라미터 검토 필요.

  1. /childs는 문법적으로 올바르지 않습니다. /children으로 변경하는 것이 좋습니다.
  2. customUserDetails 파라미터가 메서드 내에서 사용되지 않습니다. 인증 강제를 위한 것이라면 문제없지만, 그렇지 않다면 제거를 고려해 주세요.
-  @GetMapping("/childs")
+  @GetMapping("/children")
   public ResponseEntity<List<BoardResponse>> getChildBoards(
           @AuthenticationPrincipal CustomUserDetails customUserDetails) {

183-188: 응답 타입 일관성 및 반환 메시지 검토.

  1. ResponseEntity<?>는 다른 DELETE 엔드포인트(deletePost, deleteComment)가 void를 반환하는 것과 일관성이 없습니다.
  2. 성공 메시지를 문자열로 반환하는 방식은 API 응답 형식의 일관성을 해칠 수 있습니다.

다른 엔드포인트와 일관성을 맞추려면:

-  public ResponseEntity<?> deleteBoard(
+  public ResponseEntity<Void> deleteBoard(
           @PathVariable UUID boardId,
           @AuthenticationPrincipal CustomUserDetails customUserDetails) {
     UUID userId = customUserDetails.getUserId();
     postService.deleteBoard(boardId, userId);
-    return ResponseEntity.ok("게시판 삭제가 완료되었습니다.");
+    return ResponseEntity.ok().build();
   }

267-269: 불필요한 빈 줄 제거.

파일 끝에 불필요한 빈 줄이 추가되었습니다.

backend/src/main/java/org/sejongisc/backend/board/service/PostServiceImpl.java (2)

4-4: 사용되지 않는 import.

Map이 import되었지만 이 파일에서 사용되지 않습니다.

-import java.util.Map;

193-195: deletePost 호출 시 userId 우회 방식에 대한 검토.

deletePost(row.getPostId(), row.getUserId())는 게시물 작성자의 userId를 전달하여 소유권 검사를 우회합니다. 이는 동작하지만, deletePost 메서드의 의도와 다르게 사용되어 암묵적인 결합을 만듭니다.

장기적으로는 관리자 권한으로 삭제하는 별도의 내부 메서드를 고려해 볼 수 있습니다:

private void deletePostByAdmin(UUID postId) {
    // 소유권 검사 없이 삭제 로직만 수행
}
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ab6d453 and 396ed2e.

📒 Files selected for processing (7)
  • backend/src/main/java/org/sejongisc/backend/board/controller/BoardController.java (2 hunks)
  • backend/src/main/java/org/sejongisc/backend/board/repository/BoardRepository.java (1 hunks)
  • backend/src/main/java/org/sejongisc/backend/board/repository/PostRepository.java (2 hunks)
  • backend/src/main/java/org/sejongisc/backend/board/repository/projection/PostIdUserIdProjection.java (1 hunks)
  • backend/src/main/java/org/sejongisc/backend/board/service/PostService.java (1 hunks)
  • backend/src/main/java/org/sejongisc/backend/board/service/PostServiceImpl.java (4 hunks)
  • backend/src/main/java/org/sejongisc/backend/common/exception/ErrorCode.java (2 hunks)
🔇 Additional comments (5)
backend/src/main/java/org/sejongisc/backend/common/exception/ErrorCode.java (1)

90-91: LGTM!

새로운 에러 코드가 기존 패턴(INVALID_POST_OWNER, INVALID_COMMENT_OWNER)과 일관성 있게 추가되었습니다. 게시판 수정/삭제 권한 검증에 적절한 HttpStatus.FORBIDDEN 상태를 사용하고 있습니다.

backend/src/main/java/org/sejongisc/backend/board/repository/PostRepository.java (1)

29-34: LGTM!

프로젝션 기반 쿼리가 적절하게 구현되었습니다. 전체 Post 엔티티 대신 필요한 필드(postId, userId)만 조회하여 성능상 이점이 있습니다. JPQL 문법과 @Param 어노테이션 사용이 올바릅니다.

backend/src/main/java/org/sejongisc/backend/board/repository/projection/PostIdUserIdProjection.java (1)

1-8: LGTM!

Spring Data JPA 인터페이스 기반 프로젝션 패턴을 올바르게 따르고 있습니다. 메서드명이 JPQL 쿼리의 alias(postId, userId)와 정확히 일치하여 프로젝션 매핑이 정상 동작합니다.

backend/src/main/java/org/sejongisc/backend/board/repository/BoardRepository.java (1)

11-12: LGTM!

Spring Data JPA 파생 쿼리 네이밍 규칙을 올바르게 따르고 있습니다. findAllByParentBoardIsNotNull()은 하위 게시판 목록 조회에, findAllByParentBoard_BoardId()는 특정 부모의 하위 게시판 조회(cascade 삭제 시)에 적절하게 사용될 것입니다.

backend/src/main/java/org/sejongisc/backend/board/service/PostServiceImpl.java (1)

186-190: I attempted to verify the review comment by examining the Board entity, deletion logic, and hierarchy constraints, but encountered repository access issues that prevented code inspection.

Cannot verify multi-level board hierarchy concern due to inaccessible codebase.

The original review raises a valid architectural question: whether the current deletion logic (which only retrieves direct child boards) properly handles board hierarchies deeper than 2 levels. Without access to the codebase, I cannot definitively determine:

  • Whether the Board entity enforces depth constraints
  • If cascade deletion is properly configured in the JPA/Hibernate mapping
  • Whether recursive deletion logic exists elsewhere
  • The intended design (2-level vs. multi-level support)

The concern remains valid and requires manual verification of the Board entity's parentBoard relationship configuration and the complete deletion flow logic.

}


// 게시판 생성
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

주석이 잘못되었습니다.

주석이 "게시판 생성"으로 되어 있으나, 실제로는 "하위 게시판 목록 조회" 기능입니다. 복사-붙여넣기 실수로 보입니다.

-  // 게시판 생성
+  // 하위 게시판 목록 조회
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// 게시판 생성
// 하위 게시판 목록 조회
🤖 Prompt for AI Agents
In
backend/src/main/java/org/sejongisc/backend/board/controller/BoardController.java
around line 164, the inline comment currently reads "게시판 생성" but the method
actually implements "하위 게시판 목록 조회"; update the comment to accurately describe
the method (e.g., "하위 게시판 목록 조회" or the appropriate Javadoc/mapping
description), and scan nearby comments for any other copy-paste mismatches to
correct them as well.

return ResponseEntity.ok(postService.getChildBoards());
}

// 게시글 삭제
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

주석이 잘못되었습니다.

주석이 "게시글 삭제"로 되어 있으나, 실제로는 "게시판 삭제" 기능입니다.

-  // 게시글 삭제
+  // 게시판 삭제
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// 게시글 삭제
// 게시판 삭제
🤖 Prompt for AI Agents
In
backend/src/main/java/org/sejongisc/backend/board/controller/BoardController.java
around line 175, the inline comment incorrectly reads "게시글 삭제" but the method
implements deletion of a board; update the comment to "게시판 삭제" (or a more
descriptive "게시판 삭제 처리" / "Delete board") to accurately reflect the
functionality and keep any adjacent JavaDoc or method-level comments consistent.

Comment on lines +178 to +198
@Transactional
public void deleteBoard(UUID boardId, UUID boardUserId) {
User user = userRepository.findById(boardUserId).orElseThrow(() -> new CustomException(ErrorCode.USER_NOT_FOUND));
if(!user.getRole().equals(Role.PRESIDENT)){
throw new CustomException(ErrorCode.INVALID_BOARD_OWNER);
}
//상위 게시판이면 하위 게시판 목록을 조회
// 1. 부모 + 자식 boardId 목록 만들기
List<UUID> targetBoardIds = Stream.concat(
Stream.of(boardId), // 자신 포함
boardRepository.findAllByParentBoard_BoardId(boardId).stream()
.map(Board::getBoardId)
).toList();

// 2. 각 boardId마다 postId/userId 조회해서 삭제
targetBoardIds.stream()
.flatMap(id -> postRepository.findPostIdAndUserIdByBoardId(id).stream())
.forEach(row -> deletePost(row.getPostId(), row.getUserId()));
targetBoardIds.forEach(boardRepository::deleteById);
return;
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion | 🟠 Major

@Override 어노테이션 누락 및 불필요한 return 문.

deleteBoard 메서드에 @Override 어노테이션이 누락되었고, line 197의 return; 문은 불필요합니다.

+  @Override
   @Transactional
   public void deleteBoard(UUID boardId, UUID boardUserId) {
     // ... existing code ...
     targetBoardIds.forEach(boardRepository::deleteById);
-    return;
   }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
@Transactional
public void deleteBoard(UUID boardId, UUID boardUserId) {
User user = userRepository.findById(boardUserId).orElseThrow(() -> new CustomException(ErrorCode.USER_NOT_FOUND));
if(!user.getRole().equals(Role.PRESIDENT)){
throw new CustomException(ErrorCode.INVALID_BOARD_OWNER);
}
//상위 게시판이면 하위 게시판 목록을 조회
// 1. 부모 + 자식 boardId 목록 만들기
List<UUID> targetBoardIds = Stream.concat(
Stream.of(boardId), // 자신 포함
boardRepository.findAllByParentBoard_BoardId(boardId).stream()
.map(Board::getBoardId)
).toList();
// 2. 각 boardId마다 postId/userId 조회해서 삭제
targetBoardIds.stream()
.flatMap(id -> postRepository.findPostIdAndUserIdByBoardId(id).stream())
.forEach(row -> deletePost(row.getPostId(), row.getUserId()));
targetBoardIds.forEach(boardRepository::deleteById);
return;
}
@Override
@Transactional
public void deleteBoard(UUID boardId, UUID boardUserId) {
User user = userRepository.findById(boardUserId).orElseThrow(() -> new CustomException(ErrorCode.USER_NOT_FOUND));
if(!user.getRole().equals(Role.PRESIDENT)){
throw new CustomException(ErrorCode.INVALID_BOARD_OWNER);
}
//상위 게시판이면 하위 게시판 목록을 조회
// 1. 부모 + 자식 boardId 목록 만들기
List<UUID> targetBoardIds = Stream.concat(
Stream.of(boardId), // 자신 포함
boardRepository.findAllByParentBoard_BoardId(boardId).stream()
.map(Board::getBoardId)
).toList();
// 2. 각 boardId마다 postId/userId 조회해서 삭제
targetBoardIds.stream()
.flatMap(id -> postRepository.findPostIdAndUserIdByBoardId(id).stream())
.forEach(row -> deletePost(row.getPostId(), row.getUserId()));
targetBoardIds.forEach(boardRepository::deleteById);
}
🤖 Prompt for AI Agents
In
backend/src/main/java/org/sejongisc/backend/board/service/PostServiceImpl.java
around lines 178 to 198, the deleteBoard method is missing the @Override
annotation and contains an unnecessary trailing "return;" at line 197; add the
@Override annotation immediately above the method signature and remove the
redundant "return;" statement so the method ends naturally after deleting boards
and posts.

Comment on lines +338 to +346
// 하위 게시판 조회
@Transactional(readOnly = true)
public List<BoardResponse> getChildBoards() {
List<Board> childBoards = boardRepository.findAllByParentBoardIsNotNull();

return childBoards.stream()
.map(BoardResponse::from)
.toList();
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion | 🟠 Major

@Override 어노테이션 누락.

getChildBoards 메서드가 PostService 인터페이스를 구현하고 있다면 @Override 어노테이션이 필요합니다.

   // 하위 게시판 조회
+  @Override
   @Transactional(readOnly = true)
   public List<BoardResponse> getChildBoards() {
🤖 Prompt for AI Agents
In
backend/src/main/java/org/sejongisc/backend/board/service/PostServiceImpl.java
around lines 338 to 346, the getChildBoards() method is missing the @Override
annotation; if this method implements PostService, add the @Override annotation
immediately above the method signature to reflect interface implementation and
help the compiler catch signature mismatches.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant